﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;

namespace DBUtil.Util
{
    /// <summary>
    /// 映射工具
    /// </summary>
    public class MapperUtil
    {
        private static readonly ConcurrentDictionary<Type, Func<object, Dictionary<string, object>>> _getCols = new ConcurrentDictionary<Type, Func<object, Dictionary<string, object>>>();

        private static readonly ConcurrentDictionary<Type, Func<object, IDataParameter[]>> _getParas = new ConcurrentDictionary<Type, Func<object, IDataParameter[]>>();

        /// <summary>
        /// 处理映射:从对象的属性中提取列信息
        /// </summary>
        /// <param name="obj">对象实例</param>
        /// <returns></returns>
        public static Dictionary<string, object> MapDictionaryFromObject(object obj)
        {
            if (obj is Dictionary<string, object> dic) return dic;
            if (obj == null) return new Dictionary<string, object>();
            var cols = _getCols.GetOrAdd(obj.GetType(), GetCols(obj))(obj);
            return cols;
        }

        /// <summary>
        /// 处理映射:从对象的属性中提取列信息
        /// </summary>
        /// <param name="db">数据库访问对象</param>
        /// <param name="obj">对象实例</param>
        /// <returns></returns>
        public static IDataParameter[] MapParametersFromObject(DBAccess db, object obj)
        {
            if (obj is IEnumerable<IDataParameter> paras) return paras.ToArray();
            if (obj == null) return new IDataParameter[0];
            var cols = _getParas.GetOrAdd(obj.GetType(), GetParas(db, obj))(obj);
            return cols;
        }

        /// <summary>
        /// 处理映射: 将对象批量插入到表
        /// </summary>
        /// <param name="batchs"></param>
        /// <returns></returns>
        public static (string tableName, Dictionary<string, object>)[] MapDictionaryFromObjectBatch_AssignTable((string tableName, object obj)[] batchs)
        {
            var res = new List<(string tableName, Dictionary<string, object>)>();
            foreach (var item in batchs)
            {
                res.Add((item.tableName, MapDictionaryFromObject(item.obj)));
            }
            return res.ToArray();
        }

        /// <summary>
        /// 处理映射: 将对象批量插入到表
        /// </summary>
        /// <param name="batchs"></param>
        /// <returns></returns>
        public static Dictionary<string, object>[] MapDictionaryFromObjectBatch(params object[] batchs)
        {
            var res = new List<Dictionary<string, object>>();
            foreach (var item in batchs)
            {
                res.Add(MapDictionaryFromObject(item));
            }
            return res.ToArray();
        }

        /// <summary>
        /// 获取指定对象对应的参数名和值
        /// </summary>
        /// <param name="db">数据库访问对象</param>
        /// <param name="obj">指定对象</param>
        /// <returns></returns>
        private static Func<object, IDataParameter[]> GetParas(DBAccess db, object obj)
        {
            var fields = obj.GetType().GetProperties();
            var props = new List<PropertyInfo>();
            foreach (var item in fields) props.Add(item);
            return _ =>
            {
                var paras = new List<IDataParameter>();
                foreach (var item in props)
                {
                    paras.Add(db.CreatePara(item.Name, item.GetValue(_)));
                }
                return paras.ToArray();
            };
        }

        /// <summary>
        /// 获取指定对象对应的列名和值
        /// </summary>
        /// <param name="obj">指定对象</param>
        /// <returns></returns>
        private static Func<object, Dictionary<string, object>> GetCols(object obj)
        {
            var fields = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            var props = new List<PropertyInfo>();
            foreach (var item in fields) props.Add(item);
            return _ =>
            {
                var dic = new Dictionary<string, object>();
                foreach (var item in props)
                {
                    dic.Add(item.Name, item.GetValue(_));
                }
                return dic;
            };
        }

        /// <summary>
        /// 处理映射:查询结果到模型
        /// </summary>
        /// <typeparam name="T">要映射到的模型</typeparam>
        /// <param name="dic">一行查询结果</param>
        /// <returns></returns>
        public static T MappingSelect<T>(Dictionary<string, object> dic) where T : class, new()
        {
            var colNames = new List<string>();
            foreach (var item in dic) colNames.Add(item.Key);
            var map = _getPropMapper(colNames, typeof(T), true, true);
            var model = new T();
            foreach (var item in map)
            {
                var val = item.Value;
                try
                {
                    val.SetValue(model, dic[item.Key]);
                }
                catch { }
            }
            return model;
        }

        /// <summary>
        /// DataTable到模型属性的映射
        /// </summary>
        /// <param name="colNames">DataTable表</param>
        /// <param name="type">模型类型</param>
        /// <param name="isIgnoreCase">是否忽略大小写</param>
        /// <param name="isIgnoreUnderline">是否忽略下划线</param>
        /// <returns></returns>
        private static Dictionary<string, PropertyInfo> _getPropMapper(List<string> colNames, Type type, bool isIgnoreCase, bool isIgnoreUnderline)
        {
            var dic = new Dictionary<string, PropertyInfo>();
            if (type == null || dic == null) return dic;
            var props = type.GetProperties().ToList();
            //第一遍先忽略大小去匹配名称
            for (int i = colNames.Count - 1; i >= 0; i--)
            {
                var colname = colNames[i];
                for (int j = props.Count - 1; j >= 0; j--)
                {
                    if (string.Equals(colname, props[j].Name, isIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
                    {
                        dic.Add(colname, props[j]);
                        props.RemoveAt(j);
                        colNames.RemoveAt(i);
                        break;
                    }
                }
            }
            if (isIgnoreUnderline)
            {
                //第二遍忽略掉"_"符号
                for (int i = colNames.Count - 1; i >= 0; i--)
                {
                    var colname = colNames[i];
                    for (int j = props.Count - 1; j >= 0; j--)
                    {
                        var colname2 = colname.Replace("_", "");
                        var propname2 = props[j].Name.Replace("_", "");
                        if (string.Equals(colname2, propname2, StringComparison.OrdinalIgnoreCase))
                        {
                            dic.Add(colname, props[j]);
                            props.RemoveAt(j);
                            colNames.RemoveAt(i);
                            break;
                        }
                    }
                }
            }
            return dic;
        }
    }
}
